package com.skyline.common.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.skyline.common.bean.Page;

/**
 * 分页标签
 * 
 * @author wuqh
 */
public class PageTag extends TagSupport {
	private static final long serialVersionUID = 6328083803585909955L;
	private static final Log LOGGER = LogFactory.getLog(PageTag.class);
	/** 分页对象 */
	private Page page;
	/** 当前页的class(例如&lt;span class="currentPage"&gt;中的currentPage) */
	private String curClass;
	/** 当前页的style */
	private String curStyle;
	/** 非前页的style */
	private String normalClass;
	/** 当前页的style */
	private String normalStyle;
	/** 围在页数外面的字符串(写法XX%sYY，例如 第%s页，最后会变成 第2页 这样) */
	private String decStr;
	/** 页面url(xxx.html/xxx.html?a=x&b=y) */
	private String url;
	/** 当前页是否显示链接（true则当前页也是&lt;a&gt;标签，否则为&lt;span&gt;） */
	private Boolean arcCur;
	/** 显示的最多页数（例如5，则显示1,2,3,4,5或者2,3,4,5,6等等） */
	private Integer maxSize;
	/** url是否含有参数，有就在原有的基础上加&curpage=x，否侧加?curpage=x */
	private boolean hasParam;
	/** 显示最前的maxSize页，即第一页在显示范围之内，且最后一页在显示范围之外 */
	private static final int START = 1;
	/** 显示中间的maxSize页，即第一页和后一页都在显示范围之外 */
	private static final int MIDDLE = 2;
	/** 显示最后的maxSize页，即第一页在显示范围之外，且最后一页在显示范围之内 */
	private static final int END = 3;
	/** 只显示maxSize页 */
	private static final int NONE = 0;
	/** 当前显示属于START、MIDDLE、END、NONE中的哪种 */
	private int type = NONE;

	@Override
	public int doEndTag() throws JspException {
		// 多于一页才显示分页
		if (null == page || 1 == page.getTotalPage()) {
			return SKIP_BODY;
		}
		String linkStr = generateTotalStr();
		try {
			pageContext.getOut().println(linkStr);
		} catch (IOException e) {
			LOGGER.warn("调用page出错", e);
		}
		setToDefault();
		return EVAL_BODY_INCLUDE;
	}

	private void setToDefault() {
		page = null;
		curClass = null;;
		curStyle = null;
		normalClass = null;
		normalStyle = null;;
		decStr = null;
		url = null;;
		arcCur = false;
		maxSize = 5;
		hasParam = false;
		type = NONE;
	}
	
	/**
	 * 组合所有的链接，产生最后需要显示的链接
	 */
	private String generateTotalStr() {
		StringBuilder builder = new StringBuilder();
		// 必须放第一位，只有它调用getIndexAarry，刷新type
		String mainLink = generateMainLinkStr();
		String firstLink = generateFirstLinkStr();
		String preLink = generatePreLinkStr();
		String nextLink = generateNextLinkStr();
		String lastLink = generateLastLinkStr();
		// 超链接格式为 [上一页][ 1（同主体的第一页） ... ]主体（即 1 2 3 等等）[ ... X（同主体的最后一页）][下一页]
		builder.append(preLink);
		builder.append(' ');
		builder.append(firstLink);
		builder.append(' ');
		if (!firstLink.equals("")) {
			builder.append("... ");
		}
		builder.append(mainLink);
		builder.append(' ');
		if (!lastLink.equals("")) {
			builder.append("... ");
		}
		builder.append(lastLink);
		builder.append(' ');
		builder.append(nextLink);
		return builder.toString();
	}

	/**
	 * 产生上一页的链接（当前页非第一页才会有链接，否则为空串）
	 * 
	 */
	private String generatePreLinkStr() {
		if (page.isFirst()) {
			return "";
		} else {
			return generateLinkStr(true, normalClass, normalStyle, page
					.getCurpage() - 1, url, "上一页");
		}
	}

	/**
	 * 产生下一页的链接（当前页非最后页才会有链接，否则为空串）
	 * 
	 */
	private String generateNextLinkStr() {
		if (page.isLast()) {
			return "";
		} else {
			return generateLinkStr(true, normalClass, normalStyle, page
					.getCurpage() + 1, url, "下一页");
		}
	}

	/**
	 * 产生第一页的链接（第一页不在显示返回之内才产生）
	 * 
	 */
	private String generateFirstLinkStr() {
		if (type == START || type == NONE) {
			return "";
		} else {
			return generateLinkStr(true, normalClass, normalStyle, 1, url,
					decStr);
		}
	}

	/**
	 * 产生最后页的链接（最后页不在显示返回之内才产生）
	 * 
	 */
	private String generateLastLinkStr() {
		if (type == END || type == NONE) {
			return "";
		} else {
			return generateLinkStr(true, normalClass, normalStyle, page
					.getTotalPage(), url, decStr);
		}
	}

	/**
	 * 产生maxSize页的超链接
	 */
	private String generateMainLinkStr() {
		Integer[] index = getIndexAarry(page, maxSize);
		StringBuilder linkStr = new StringBuilder();
		int length = index.length;
		for (int i = 0; i < length; i++) {
			if (index[i] == page.getCurpage()) {
				linkStr.append(generateLinkStr(arcCur, curClass, curStyle, page
						.getCurpage(), url, decStr));
			} else {
				linkStr.append(generateLinkStr(true, normalClass, normalStyle,
						index[i], url, decStr));
			}
			linkStr.append(' ');
		}
		return linkStr.toString();
	}

	/**
	 * 产生某一页的超链接
	 * 
	 * @param arcCur
	 *            该页是否是超链接，是就用&lt;a&gt;否则用&lt;span&gt;
	 * @param curClass
	 *            该页的class属性
	 * @param curStyle
	 *            该页的style属性
	 * @param curpage
	 *            该页是第几页
	 * @param url
	 *            该页的超链接
	 * @param decStr
	 *            围在页数外面的字符串(写法XX%sYY，例如 第%s页，最后会变成 第2页 这样)
	 */
	private String generateLinkStr(Boolean arcCur, String curClass,
			String curStyle, Integer curpage, String url, String decStr) {
		if (arcCur == null) {
			arcCur = false;
		}
		// 把decStr结合页码后产生的串
		String dec = curpage.toString();
		decStr = StringUtils.trimToNull(decStr);
		if (null != decStr) {
			dec = decStr.replace("%s", dec);
		}
		StringBuilder cur = new StringBuilder();
		if (arcCur) {
			// url是否含有参数，有就在原有的基础上加&curpage=x，否侧加?curpage=x
			cur.append("<a href=\"").append(url);
			if (hasParam) {
				cur.append('&');
			} else {
				cur.append('?');
			}
			cur.append("curpage=").append(curpage).append('\"');
		} else {
			cur.append("<span");
		}
		if (null != StringUtils.trimToNull(curClass)) {
			cur.append(" class=\"").append(curClass.trim()).append('\"');
		}
		if (null != StringUtils.trimToNull(curStyle)) {
			cur.append(" style=\"").append(curStyle.trim()).append('\"');
		}
		cur.append('>').append(dec).append("</");
		if (arcCur) {
			cur.append("a>");
		} else {
			cur.append("span>");
		}
		return cur.toString();
	}

	/**
	 * 获取需要的页的数组(同时刷新type)，比如：一共10页，最多显示5页，当前页为第6也，那么显示[4,5,6,7,8]这几页，如果当前页为9，那么显示[6,7,8,9,10]
	 * 
	 * @param page
	 *            分页类，用于获取总页数和当前页数
	 * @param maxSize
	 *            最多显示多少页
	 */
	private Integer[] getIndexAarry(Page page, Integer maxSize) {
		int total = page.getTotalPage();
		int current = page.getCurpage();
		// 如果总页数小于最多显示页数，则全部显示
		if (total <= maxSize) {
			type = NONE;
			Integer[] index = new Integer[total];
			for (int i = 0; i < total; i++) {
				index[i] = i + 1;
			}
			return index;
		}

		/**
		 * <pre>
		 * 分情况讨论： 
		 * (1)、何时永远显示1...maxSize：
		 * 		如果maxSize为偶数，则当前页在1...maxSize/2时；
		 * 		如果maxSize为奇数，则当前页为1...(maxSize/2 +1)时，
		 * 		故得当前页为(maxSize + 1) / 2
		 * (2)、何时永远显示最后maxSize页：
		 * 		不论奇偶，当前页在(total-maxSize/2)...total时
		 * (3)、中间时从哪页开始显示：
		 * 		如果maxSize为偶数，则从 当前页-maxSize/2+1 开始显示
		 * 		如果maxSize为奇数，则从 当前页-maxSize/2 开始显示
		 * 		故从 当前页-(maxSize+1)/2+1 开始显示
		 * </pre>
		 */
		// 当前页靠前则middle=(maxSize + 1) / 2;否则middle = maxSize / 2;
		// 前面几页不需要动
		int middle = (maxSize + 1) / 2;
		if (current <= middle) {
			type = START;
			Integer[] index = new Integer[maxSize];
			for (int i = 0; i < maxSize; i++) {
				index[i] = i + 1;
			}
			return index;
		}
		// 后面几页也不需要动
		middle = maxSize / 2;
		if ((current + middle) >= total) {
			type = END;
			Integer[] index = new Integer[maxSize];
			for (int i = 0, value = (total - maxSize + 1); i < maxSize; i++, value++) {
				index[i] = value;
			}
			return index;
		}
		// 中间的需要动
		type = MIDDLE;
		middle = (maxSize + 1) / 2;
		Integer[] index = new Integer[maxSize];
		int startIndex = current - middle + 1;
		for (int i = 0; i < maxSize; i++, startIndex++) {
			index[i] = startIndex;
		}
		return index;
	}

	public void setPage(Page page) {
		this.page = page;
	}

	public void setCurClass(String curClass) {
		this.curClass = curClass;
	}

	public void setCurStyle(String curStyle) {
		this.curStyle = curStyle;
	}

	public void setNormalClass(String normalClass) {
		this.normalClass = normalClass;
	}

	public void setNormalStyle(String normalStyle) {
		this.normalStyle = normalStyle;
	}

	public void setUrl(String url) {
		this.url = url;
		if (url.indexOf('?') != -1) {
			hasParam = true;
		} else {
			hasParam = false;
		}
	}

	public void setArcCur(Boolean arcCur) {
		this.arcCur = arcCur;
	}

	public void setMaxSize(Integer maxSize) {
		this.maxSize = maxSize;
	}

	public void setDecStr(String decStr) {
		this.decStr = decStr;
	}

	public static void main(String... args) {
		Page page = new Page();
		page.setCurpage(4);
		page.setSize(20);
		page.setTotal(200);
		PageTag tag = new PageTag();
		tag.setPage(page);
		tag.setMaxSize(5);
		tag.setUrl("*.do");
		System.out.println();
		System.out.println(page.getCurpage());
		System.out.println(tag.generateMainLinkStr());
		System.out.println(tag.generatePreLinkStr());
		System.out.println(tag.generateNextLinkStr());
		System.out.println(tag.generateFirstLinkStr());
		System.out.println(tag.generateLastLinkStr());
		System.out.println(tag.generateTotalStr());
	}
}
